//
//$Id: nfamin_omp.cpp 722 2012-11-29 17:33:43Z andrew.tsyganov $
#include "stdafx.h"
#include "contrib/simpleini/SimpleIni.h"
#include "../src/nfamin/include/utils.h"
#include "../src/nfamin/include/kw_omp.h"
#include "../src/nfamin/include/mel_omp.h"
#include "../src/nfamin/include/combgen.h"
#include <omp.h>
#include "../src/nfamin/include/my_time.h"
#include "contrib/bm/src/bm.h"
#include "contrib/bm/src/bmserial.h"
#include "../src/nfamin/include/re.h"
#include <bitset>
#include "conio.h"

using namespace std;
using namespace kw;
using namespace mel;

void MinimizeKW(vector<FA> const& vfa, KW_Parameters const& p, string const& output_dir, string const& save_format, int rand_seed)
{
    //Subset s;
    //s.insert(301);

    for (size_t i = 0; i < vfa.size(); ++i)
    {
        //if (!s.contains(i))
        //    continue;

        if (p.verbosity_)
        {
            PrintLine(cout, 128, '='); 

            if (vfa.size() > 1) 
            {
                cout << endl
                     << "FA #" << i << endl;
            }
        }
        else
        {
            if (i == 0)
            {
                cout << "FA  #  |MDA size|RAM rows|RAM cols|Ones dens.|Grids fnd.|Grids time, s|Min. cov.|Max. cov.|Min. NFA fnd.|Min. NFA size|Total time, s|" << endl
                     << endl;
            }

            cout << setw(5) << i << flush;
        }

        vfa[i].save(output_dir, vfa[i].name_, save_format);

        KW_OMP kw(vfa[i], p, omp_get_max_threads()); 
        
        if (!kw.solve(rand_seed))
        {
            return;
        }

        /*
        FA_Stat fa_st;
        Subset::statistics s_st;
        size_t mem = 0;

        kw.da_.calc_Stat(fa_st);
        fa_st.print(cout, MB);
        for (size_t j = 0; j < kw.vs_a_.size(); ++j)
        {
            kw.vs_a_[j].calc_stat(&s_st);
            mem += s_st.memory_used;
        }

        cout << "vs_a_:     " << mem /(1024*1024) << " Mb" << endl;
        */

        if (p.verbosity_)
        {
            if (kw.stat_.min_a_count_)
            {
                cout << endl 
                     << kw.stat_.min_a_count_ << " minimum state NFA(s) found" << endl;
                
                for (size_t j = 0; j < kw.v_min_a_.size(); ++j)
                {
                    kw.output_FA(cout, kw.v_min_a_[j]);
                }
                
                cout << endl
                     << "Total time = " << kw.stat_.total_time_ << " s" << endl;
            }
            else
            {
                cout << endl
                     << "Minimum state NFA(s) not found!" << endl
                     << endl
                     << "Total time = " << kw.stat_.total_time_ << " s" << endl;
            }
        }
        else
        {
            cout << setw(12) << kw.stat_.min_a_count_ << setw(15) << kw.stat_.min_a_size_ 
                 << setprecision(4) << setw(16) << kw.stat_.total_time_ << endl;
        }

        for (size_t j = 0; j < kw.v_min_a_.size(); ++j)
        {
            kw.v_min_a_[j].save(output_dir, vfa[i].name_ + "_min_" + IntToStringZ(j, kw.stat_.min_a_count_ - 1), save_format);
        }
    }
}

void MinimizeMel(vector<FA> const& vfa, Mel_Parameters const& p, string const& output_dir, string const& save_format, int rand_seed)
{
    //Subset s;
    //s.insert(301);

    for (size_t i = 0; i < vfa.size(); ++i)
    {
        //if (!s.contains(i))
        //    continue;

        if (p.verbosity_)
        {
            PrintLine(cout, 128, '='); 

            if (vfa.size() > 1) 
            {
                cout << endl
                     << "FA #" << i << endl;
            }
        }
        else
        {
            if (i == 0)
            {
                cout << "FA  #  |MDA size|Rel rows|Rel cols|Ones dens.|Grids fnd.|Grids time, s|Min. cov.|Max. cov.|Min. NFA fnd.|Min. NFA size|Total time, s|" << endl
                     << endl;
            }

            cout << setw(5) << i << flush;
        }

        vfa[i].save(output_dir, vfa[i].name_, save_format);

        Mel_OMP mel(vfa[i], p, omp_get_max_threads()); 
        
        if (!mel.solve(rand_seed))
        {
            return;
        }

        if (p.verbosity_)
        {
            if (mel.stat_.min_a_count_)
            {
                cout << endl 
                     << mel.stat_.min_a_count_ << " minimum state NFA(s) found" << endl;
                
                for (size_t j = 0; j < mel.v_min_a_.size(); ++j)
                {
                    mel.output_FA(cout, mel.v_min_a_[j]);
                }
                
                cout << endl
                     << "Total time = " << mel.stat_.total_time_ << " s" << endl;
            }
            else
            {
                cout << endl
                     << "Minimum state NFA(s) not found!" << endl
                     << endl
                     << "Total time = " << mel.stat_.total_time_ << " s" << endl;
            }
        }
        else
        {
            cout << setw(12) << mel.stat_.min_a_count_ << setw(15) << mel.stat_.min_a_size_ 
                 << setprecision(4) << setw(16) << mel.stat_.total_time_ << endl;
        }

        for (size_t j = 0; j < mel.v_min_a_.size(); ++j)
        {
            mel.v_min_a_[j].save(output_dir, vfa[i].name_ + "_min_" + IntToStringZ(j, mel.stat_.min_a_count_ - 1), save_format);
        }
    }
}

int main(int argc, char* argv[])
{
/*
    RE r;
    r.init("(a+b.c)*.a*.(c-d)");
*/
/*
    Subset s;
    Subset::statistics st;
    s.calc_stat(&st);
    cout << sizeof(s) << endl;
    cout << st.memory_used << endl;
    s.insert(0);
    s.insert(10);
    s.insert(100);
    s.insert(1000);
    cout << sizeof(s) << endl;
    cout << st.memory_used << endl;
    s.clear();
    cout << sizeof(s) << endl;
    cout << st.memory_used << endl;

    return 0;
*/    
    if (argc < 3)
    {
        cout << "nfamin_omp usage: nfamin_omp.exe file.ini output_dir [rand_seed]" << endl;
        return -1;
    }

    int rand_seed;
    
    if (argc == 3)
    {
        rand_seed = 0;
    }
    else
    {
        rand_seed = atoi(argv[3]);
    }

    CSimpleIniA ini;
    bool load_ok = (ini.LoadFile(argv[1]) >= SI_OK);
    if (!load_ok) 
    {
        cout << "nfamin_omp: *.ini file error!" << endl;
        return -1;
    }

    string input_folder = ini.GetValue("nfamin", "input_folder");
    string save_format  = ini.GetValue("nfamin", "save_format");
    string method       = ini.GetValue("nfamin", "method");
 
    vector<FA> vfa;
    LoadFAs(vfa, input_folder);

    if (method == "mel")
    {
        Mel_Parameters p(ini);
        MinimizeMel(vfa, p, argv[2], save_format, rand_seed);
    }
    else
    {
        if (method == "kw")
        {
            KW_Parameters p(ini);
            MinimizeKW(vfa, p, argv[2], save_format, rand_seed);
        }
        else
        {
            cout << "nfamin_omp: unknown method!" << endl;
        }
    }

	//void If_Debug_Press_Enter();
	//If_Debug_Press_Enter();
	_getch();
    return 0;
}
//EOF!